package com.hsc.mvc.config;

import com.hsc.mvc.Configuration;
import com.hsc.mvc.annotations.Controller;
import com.hsc.mvc.annotations.InterceptorAspect;
import com.hsc.mvc.annotations.RequestMapping;
import com.hsc.mvc.bind.ParameterBinder;
import com.hsc.mvc.conversion.Convertor;
import com.hsc.mvc.tag.AopMappingTag;
import com.hsc.mvc.tag.ConvertorTag;
import com.hsc.mvc.tag.MappingTag;
import com.hsc.mvc.tag.ParamBinderTag;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

/**
 * @author
 * @date 2025-05-18
 * @Description 基于注解的配置方式 策略模式中的策略类。ConfigReader 规定了读取配置文件的方法
 */
public class AnnotationReader extends AbstractReader implements ConfigReader {
    /**
     * 必须重写AbstractXMLReader构造方法 对AbstractReader 构造器进行调用
     * 然后存储 configuration
     */
    public AnnotationReader(Configuration configuration) {
        super(configuration);
    }

    /**
     * 读取需要扫描的包
     * <param-value>
     * com.controller,
     * com.interceptor,
     * com.service,
     * com.dao
     * </param-value>
     *
     * @param configLocation
     */
    @Override
    public void read(String configLocation) {
        // 处理字符串信息    去除空格 换行 等
        // 正则表达式 匹配 "[\\r\\n\\s]" 表示 回车 换行 空格
        String configPackage = configLocation.replaceAll("[\\r\\n\\s]", "");
        // 按照逗号进行分割
        String[] packages = configPackage.split(",");
        for (String packagePath : packages) {
            // //扫描包中的所有类，通过反射处理
            packageScan(packagePath);
        }

    }

    /**
     * ● 如何进行包扫描
     * ○ 反射无法通过包路径获得包对象，也无法通过包对象直接反射获得包中所有的类
     * ○ 所以无法使用技术直接达到效果。
     * ○ 曲线救国：
     * ■ 先根据包路径
     * ■ 获得对应的文件夹路径
     * ■ 再利用File技术获得文件夹中所有的类文件
     * ■ 类文件的名字就是类名
     * ■ 和包路径就可以组合成完整的类路径了。
     *
     * @param packagePath
     */
    private void packageScan(String packagePath) {
        //com.controller
        //com/controller 相对路径
        // 路径进行处理
        String dirPath = packagePath.replace(".", "/");
        // 得到的路径 C:\Java\code\hsc-mvc\out\artifacts\hsc_web_war_exploded\WEB-INF\classes\com\controller\
        try {
            dirPath = Thread.currentThread().getContextClassLoader().getResource(dirPath).getPath();
        } catch (NullPointerException e) {
            //一旦出现异常，说明指定的包路径不存在。 可以接收，所以不抛异常，但需要给使用者提示
            //
            System.out.println("\33[33mpackage path not exist [" + packagePath + "]\33[m");
            return;
        }
        // 得到对应的绝对路径包 我们要的是 class 文件 TestController.class
        // 代码至此  说明包路径存在
        File file = new File(dirPath);
        // 获取包下的所有文件映射信息
        File[] files = file.listFiles();
        for (File f : files) {
            // 获取 f 对应的文件名
            String childName = f.getName();
            if (f.isDirectory()) {
                // 继续扫描 递归调用
                packageScan(dirPath + "." + f.getName());
            } else {
                // 得到的路径 C:\Java\code\hsc-mvc\out\artifacts\hsc_web_war_exploded\WEB-INF\classes\com\controller\TestController.class
                // 处理文件 文件名以.class结尾的文件 才是我们要的
                if (childName.endsWith(".class")) {
                    // 说明是一个类文件
                    // 去掉.class 后缀
                    childName = childName.replaceAll(".class", "");
                    // 得到的是类名 TestController
                    // 类名  包名 + 类名
                    // 得到的类全名  com.controller.TestController
                    String className = packagePath + "." + childName;
                    // 读取类 根据类进行反射 获取到对应的注解
                    readClass(className);
                }
            }
        }
    }

    /**
     * 根据注解进行反射 获取注解信息
     *
     * @param className
     */
    private void readClass(String className) {
        try {
            Class<?> aClass = Class.forName(className);
            // 获取不同的注解 然后进行读取
            // @InterceptorAspect 注解
            // @Controller 注解
            Annotation annotation;
            if ((annotation = aClass.getAnnotation(Controller.class)) != null) {
                //说明是一个controller类
                readController(aClass);
            } else if ((annotation = aClass.getAnnotation(InterceptorAspect.class)) != null) {
                //说明是一个interceptor类
                readInterceptor(aClass);
            } else if ((annotation = aClass.getAnnotation(Convertor.class)) != null) {
                // 说明是一个类型转换器
                readConvertor(aClass);
            } else if ((annotation = aClass.getAnnotation(ParameterBinder.class)) != null) {
                // 说明是一个参数绑定器
                readParameterBinder(aClass);
            }


        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 参数绑定器
     * 添加到configuration中 统一进行管理
     * @param aClass
     */
    private void readParameterBinder(Class<?> aClass) {
        ParamBinderTag paramBinderTag = new ParamBinderTag();
        paramBinderTag.setBinderClass(aClass);
        configuration.addBinder(paramBinderTag);
    }

    /**
     * 类型转换器
     * 添加到configuration中 统一进行管理
     * @param aClass
     */
    private void readConvertor(Class<?> aClass) {
        ConvertorTag convertorTag = new ConvertorTag();
        convertorTag.setConvertorClass(aClass);
        configuration.addConvertor(convertorTag);
    }

    /**
     * 解析interceptor 注标注的类 并且保存在configuration中
     *
     * @param aClass
     */
    private void readInterceptor(Class<?> aClass) {
        InterceptorAspect annotation = aClass.getAnnotation(InterceptorAspect.class);
        String include = annotation.include();
        String exclude = annotation.exclude();
        // 保存到configuration中
        AopMappingTag aopMappingTag = new AopMappingTag();
        aopMappingTag.setAopClass(aClass);
        //  //  "/test1.do,/test2.do,/test3.do"   表示拦截的url
        // 由于是多个 所有我们进行处理
        aopMappingTag.setExclude(exclude);
        aopMappingTag.setInclude(include);

        //还需要将切面信息对象缓存起来，需要存储在configuration中。
        //configuration 父类继承而来
        configuration.putAopMapping(aopMappingTag);

    }

    /**
     * 解析controller 注解标注的类 并且保存在configuration中
     *
     * @param aClass
     */
    private void readController(Class<?> aClass) {
        // 获取类上的所有方法
        Method[] methods = aClass.getMethods();
        for (Method method : methods) {
            // 获取方法上的注解
            RequestMapping annotation = method.getAnnotation(RequestMapping.class);
            if (annotation == null) {
                continue;
            }
            // 处理注解
            String nameStr = annotation.value();
            MappingTag mappingTag = new MappingTag();
            // 设置url
            mappingTag.setNameStr(nameStr);
            // 设置Class对象
            mappingTag.setMappingClass(aClass);
            // 设置Method对象
            mappingTag.setMethod(method);
            // 保存到configuration中
            configuration.putMapping(mappingTag);
        }
    }
}
